package pers.cz.redis;

import pers.cz.commons.search.Trie;
import pers.cz.mapper.FileUploadBusiMapper;
import pers.cz.mapper.KnowLedgeBaseBusiMapper;
import pers.cz.mapper.SystemParamsMapper;
import pers.cz.minio.MinioUtils;
import pers.cz.model.FileUploadBusi;
import pers.cz.model.KnowLedgeBaseBusi;
import pers.cz.model.SystemParams;
import pers.cz.model.SystemParamsKey;
import pers.cz.spring.SpringContextUtil;
import com.google.gson.Gson;
import com.qianxinyao.analysis.jieba.keyword.Keyword;
import com.qianxinyao.analysis.jieba.keyword.TFIDFAnalyzer;
import io.minio.errors.*;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.poi.hwpf.extractor.WordExtractor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPubSub;

import java.io.*;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.List;

/**
 * reids消息队列处理线程类，该类应该由消费者去调用
 * @program: PostGirl-panent
 * @description: MessageDealThread
 * @author: Cheng Zhi
 * @create: 2021-07-03 15:15
 **/
public class MessageQueueDealThread extends Thread {

    Logger log = LoggerFactory.getLogger(MessageQueueDealThread.class);
    // 订阅的频道
    private String channel;

    // redis对象
    private Jedis resource;
    // 订阅模式需要单独的redis连接处理
    private Jedis resourceDtl;

    public MessageQueueDealThread(String channel, Jedis resource, Jedis resourceDtl) {
        this.channel = channel;
        this.resource = resource;
        this.resourceDtl = resourceDtl;
    }

    @Override
    public void run() {
        // todo 这里应该改为阻塞模式，死循环浪费资源
        /*while (true) {
            try {
                MyJedisPubSub myJedisPubSub = new MyJedisPubSub(resourceDtl);
                resource.subscribe(myJedisPubSub, "channel:" + channel);
            } catch(Exception e) {
                log.error("redis消费消息发生异常:" + e.getMessage());
            }
        }*/
    }
}

/**
 * 这里重写JedisPubSub的消息处理类，用来真正的处理消息
 */
class MyJedisPubSub extends JedisPubSub {

    private Logger logger = LoggerFactory.getLogger(MyJedisPubSub.class);

    private final String BACK_NAME = "post-girl";
    private final String OBJECT_NAME = "knowLedgeBase/";

    private Jedis jedis;
    MyJedisPubSub(Jedis jedis) {
        this.jedis = jedis;
    }

    @Override
    public void onMessage(String channel, String message) {
        System.out.println(Thread.currentThread().getName()+"-接收到消息:channel=" + channel + ",message=" + message);

        try {
            // 这里调用具体的业务处理;
            // 1、如果是文件处理频道
            if ("channel:message".equals(channel)) {
                FileUploadBusi fileUploadBusi = SpringContextUtil.getBean(FileUploadBusiMapper.class).selectByPrimaryKey(Long.valueOf(message));
                // 文件名
                String fileName = fileUploadBusi.getFileName();
                // 作者
                String author = fileUploadBusi.getUserName();
                // 流水号
                Long soNbr = fileUploadBusi.getSoNbr();
                // 文件类型
                String fileType = "";
                // 摘要
                String abstrctSentence = "";
                // 标签
                String label = "";
                // 关键字
                String keyWord = "";

                // 从oss服务器下载文件
                MinioUtils minioUtils = SpringContextUtil.getBean(MinioUtils.class);
                InputStream inputStream = minioUtils.getObject(BACK_NAME, OBJECT_NAME + fileName);
                String text = "";
                if (fileName.contains(".txt") || fileName.contains(".TXT") || fileName.contains(".sql")) {
                    // 处理普通文本文件
                    fileType = "txt";
                    text = analyzeTxt(inputStream);
                    System.out.println(text);
                } else if (fileName.contains(".doc")) {
                    fileType = "doc";
                    WordExtractor wordExtractor = new WordExtractor(inputStream);
                    //text = wordExtractor.getText();
                    System.out.println("这里屏蔽了一段东西");

                } else if (fileName.contains(".pdf")) {
                    fileType = "pdf";
                    PDDocument pdDocument;
                    pdDocument = PDDocument.load(inputStream);
                    PDFTextStripper stripper = new PDFTextStripper();
                    text = stripper.getText(pdDocument);
                    pdDocument.close();
                }

                // 将文件名也插入进去
                Trie trieTitle = Trie.getTrie();
                trieTitle.insert(fileName);

                // 进行分词，统计词频，将词频高的词统计出来
                int topN=10;
                TFIDFAnalyzer tfidfAnalyzer=new TFIDFAnalyzer();
                List<Keyword> list=tfidfAnalyzer.analyze(text,topN,false);
                StringBuilder keyWordBuilder = new StringBuilder();
                for(Keyword word:list) {
                    //方案一，
                    // 将关键字存储到redis中，用于后续输入联想功能
                    // jedis.zadd("search",0,word.getName());

                    // 方案二
                    // 在内存中构建一颗字典树，将关键字存储到字典树中，然后将树存储到redis中。
                    String searchTrie = jedis.get("searchTrie");
                    SystemParams params1 = SpringContextUtil.getBean(SystemParamsMapper.class).selectParamsByParamName("SHEARCH_TRIE");
                    SystemParamsKey key = new SystemParamsKey();

                    if (params1.getParamValue() != null && !"".equals(params1.getParamValue())) {
                        Trie trie = Trie.getTrie();
                        trie.setTrieNode(params1.getParamValue());
                        trie.insert(word.getName());
                        Gson gson = new Gson();
                        SystemParams params = new SystemParams();
                        params.setParamValue(gson.toJson(trie.root));
                        params.setParamName("SHEARCH_TRIE");
                        SpringContextUtil.getBean(SystemParamsMapper.class).updateByParamName(params);
                    } else {
                        Trie trie = Trie.getTrie();
                        trie.insert(word.getName());
                        Gson gson = new Gson();
                        SystemParams params = new SystemParams();
                        params.setParamValue(gson.toJson(trie.root));
                        params.setParamName("SHEARCH_TRIE");
                        SpringContextUtil.getBean(SystemParamsMapper.class).updateByParamName(params);
                        jedis.set("searchTrie",gson.toJson(trie.root));
                    }

                    // 方案三
                    Trie trie = Trie.getTrie();
                    trie.insert(word.getName());

                    // 将关键字用;号隔开，存储到数据库
                    keyWordBuilder.append(word.getName());
                    keyWordBuilder.append(";");
                }
                keyWord = keyWordBuilder.toString();
                // 插入数据库
                insertKnowLedgeBaseBusi(soNbr, author, fileType, keyWord, fileName);
            }

            // 2、如果是启动测试消息频道
            if ("channel:startTest".equals(channel)) {

            }
            Thread.sleep(10000L);
        } catch (Exception e) {
            logger.error("handle redis subscribe message failed!", e);
        }
    }


    /**
     * 解析txt、sql等普通文本
     * @param inputStream
     * @return
     */
    private String analyzeTxt(InputStream inputStream) throws IOException {

        StringBuilder sb = new StringBuilder();
        // 文件中可能含有中文，如果使用byte的话可能会出现读取到半个中文字符，会出现乱码
        char[] bytes = new char[12];
        int len = 0;
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream,"UTF-8");
        while ((len = inputStreamReader.read(bytes)) != -1) {
            String str = new String(bytes, 0, len);
            sb.append(str);
        }

        return sb.toString();
    }

    /**
     * 将文件信息插入数据库
     * @param soNbr
     * @param author
     * @param fileType
     * @param keyWord
     */
    private void insertKnowLedgeBaseBusi(Long soNbr, String author, String fileType, String keyWord, String fileName) {
        KnowLedgeBaseBusi knowLedgeBaseBusi = new KnowLedgeBaseBusi();
        knowLedgeBaseBusi.setSoNbr(Integer.valueOf(Math.toIntExact(soNbr)));
        knowLedgeBaseBusi.setAuthor(author);
        knowLedgeBaseBusi.setFileType(fileType);
        knowLedgeBaseBusi.setKeyword(keyWord);
        knowLedgeBaseBusi.setUpdatedTime(new Date());
        knowLedgeBaseBusi.setExt1(fileName);
        SpringContextUtil.getBean(KnowLedgeBaseBusiMapper.class).insert(knowLedgeBaseBusi);
    }



}
